home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / GraphicViewers / ViewGif2 / Source / DecodeGIF.m < prev    next >
Text File  |  1991-08-14  |  15KB  |  419 lines

  1. /*****************************************************************************/
  2. /* DecodeGIF.m                                     */
  3. /* implementation file of DecodeGIF class of ViewGif2 application         */
  4. /* Handles decoding of GIF files, and decoding status panel             */
  5. /* January 1990  Carl F. Sutter                             */
  6. /*****************************************************************************/
  7.  
  8. #import "DecodeGIF.h"
  9. #import <appkit/Application.h>
  10. #import <appkit/Window.h>
  11. #import <appkit/Control.h>
  12. #import <appkit/Panel.h>
  13. #import <appkit/Slider.h>        // for setMinValue, setMaxValue
  14. #import <appkit/tiff.h>            // for NXImageBitmap
  15. #import <stdlib.h>            // for malloc and free
  16. #import <stdio.h>            // for fprintf
  17. #import <string.h>            // for strcpy
  18.  
  19. /* some external function references to do the actual LZW decoding */
  20. void StartLZW( BYTE byCodeSizeIn, NXStream *stream,
  21.                BYTE *byR, BYTE *byG, BYTE *byB, BYTE *byMap,
  22.                int width, int height, BOOL bInter );
  23. long ContinueLZW( int nNumCodes );
  24.  
  25. @implementation DecodeGIF
  26.  
  27. /*****************************************************************************/
  28. /* new - factory method                                 */
  29. /*****************************************************************************/
  30. + new
  31.    {
  32.    self = [super new];
  33.    [self setup];
  34.    return( self );
  35.    } /* new 1/22/90 CFS */
  36.  
  37.  
  38. /*****************************************************************************/
  39. /* new - factory method    with target and action                     */
  40. /*****************************************************************************/
  41. + new:(id )targ action:(SEL )act 
  42.    {
  43.    self = [super new];
  44.    [self setup];
  45.    target = targ;
  46.    action = act;
  47.    return( self );
  48.    } /* new:action: 1/23/90 CFS */
  49.   
  50.  
  51. /*****************************************************************************/
  52. /* setup - initialize instance variables and panel etc.               */
  53. /*****************************************************************************/
  54. - setup
  55.    {
  56.    /* load nib defining panel and outlets */
  57.    [NXApp loadNibSection:"DecodeGIF.nib" owner:self];
  58.    bDecoding = NO;
  59.    return( self );
  60.    } /* setup 1/23/90 CFS */
  61.   
  62.  
  63. /*****************************************************************************/
  64. /* outlet initialization methods                            */
  65. /*****************************************************************************/
  66. - setDecodePanel:anObject { decodePanel  = anObject; return( self ); }
  67. - setGauge:anObject       { gauge        = anObject; return( self ); }
  68. - setWidth:anObject       { width        = anObject; return( self ); }
  69. - setHeight:anObject      { height       = anObject; return( self ); }
  70. - setNumColors:anObject   { numColors    = anObject; return( self ); }
  71. - setFilename:anObject    { filename     = anObject; return( self ); }
  72.  
  73.  
  74. /*****************************************************************************/
  75. /* show - display the decoder panel                         */
  76. /*****************************************************************************/
  77. - show:sender
  78.    {
  79.    [decodePanel makeKeyAndOrderFront:self];
  80.    return( self );
  81.    } /* show 1/23/90 CFS */
  82.  
  83.  
  84. /*****************************************************************************/
  85. /* setTarget - sets the object to return queue items to              */
  86. /*****************************************************************************/
  87. - setTarget:(id)targ
  88.    {
  89.    target = targ;
  90.    return( self );
  91.    } /* setTarget 1/22/90 CFS */
  92.  
  93.  
  94. /*****************************************************************************/
  95. /* setAction - sets the method that will be called with the new item         */
  96. /* there will be one parameter - a pointer to a standard C string          */
  97. /*****************************************************************************/
  98. - setAction:(SEL)aSelector
  99.    {
  100.    action = aSelector;
  101.    return( self );
  102.    } /* setAction 1/22/90 CFS */
  103.    
  104.    
  105. /*****************************************************************************/
  106. /* decodeFile - called from controller to initiate decoding process         */
  107. /*****************************************************************************/
  108. - decodeFile:(const char *)fileName
  109.    {
  110.    strcpy( szPathName, fileName );
  111.    strcpy( szFileName, strrchr( fileName, '/' ) + 1);
  112.    [filename setStringValue:szFileName];
  113.    nStatus = STATUS_OPEN;
  114.    /* use the timer to break up the decoding process and allow other events */
  115.    timer = [[Animator alloc] initChronon:0.0 adaptation:0.0 target:self
  116.                      action:@selector(nextStep:) autoStart:YES
  117.                    eventMask:NX_ALLEVENTS];
  118.    return( self );
  119.    } /* decodeFile 1/23/90 CFS */
  120.    
  121.  
  122. /*****************************************************************************/
  123. /* nextStep - called by timer, parse control to current task             */
  124. /*****************************************************************************/
  125. - nextStep:sender
  126.    {
  127.    #define    CODES_PER_ITERATION 300
  128.    long    lReturn;
  129.    
  130.    /* first, go right to the decoder, if LZW decoding is in progress */
  131.    if (bDecoding)
  132.       {
  133.       lReturn = ContinueLZW( CODES_PER_ITERATION );
  134.       if ((lReturn == 0) || (lReturn == -1)) [self finishDecoder:lReturn];
  135.       else [gauge setFloatValue:(float)lReturn];
  136.       }
  137.    /* otherwise, go to the proper step in the rest of the decoding process */   
  138.    else switch (nStatus)
  139.       {
  140.       case STATUS_OPEN:      [self openFile];          break;
  141.       case STATUS_GLOBAL:    [self readGlobalInfo];    break;
  142.       case STATUS_BLOCK:     [self readBlockCode];     break;
  143.       case STATUS_IMAGE:     [self readImageInfo];     break;
  144.       case STATUS_DECODING:  [self startDecoder];      break;
  145.       case STATUS_EXTENSION: [self cancelDecoding];    break;  // FIX THIS
  146.       case STATUS_DONE:      [self sucessfulDecoding]; break;  // ALLOW MULT IMAGES
  147.       }
  148.    return( self );
  149.    } /* nextStep 1/23/90 CFS */
  150.  
  151.  
  152. /*****************************************************************************/
  153. /* openFile - open the stream for decoding                     */
  154. /*****************************************************************************/
  155. - (BOOL)openFile
  156.    {
  157.    if (!(stream = NXMapFile( szPathName, NX_READONLY )))
  158.       {
  159.       [self errorAlert:"Could not open GIF file."];
  160.       [self cancelDecoding];
  161.       return( NO );
  162.       }
  163.    nStatus = STATUS_GLOBAL;   
  164.    return( YES );
  165.    } /* openFile 1/23/90 CFS */
  166.  
  167.       
  168. /***************************************************************************/
  169. /* readGlobalInfo - read the global header from the file           */
  170. /***************************************************************************/
  171. - (BOOL)readGlobalInfo
  172.    {
  173.    BYTE    b1, b2;
  174.    char    szSignature[7];
  175.    struct {
  176.           unsigned  map:1;
  177.           unsigned  res:3;
  178.           unsigned  reserved:1;
  179.           unsigned  bpp:3;
  180.           } globalInfo;
  181.  
  182.    /* read 6 byte version signature */
  183.    if (![self readBytes:6 data:szSignature]) return( NO );
  184.    szSignature[6] = 0;
  185.    if (0 != strcmp( szSignature, "GIF87a"))
  186.       {
  187.       [self errorAlert:"Not a GIF file - signature not GIF87a."];
  188.       [self cancelDecoding];
  189.       return( NO );
  190.       }
  191.    
  192.    /* read screen width and height */
  193.    if (![self readBytes:1 data:&b1]) return( NO );
  194.    if (![self readBytes:1 data:&b2]) return( NO );
  195.    if (![self readBytes:1 data:&b1]) return( NO );
  196.    if (![self readBytes:1 data:&b2]) return( NO );
  197.  
  198.    /* read info byte */ 
  199.    if (![self readBytes:1 data:&globalInfo]) return( NO );
  200.    //   GlobalInfo.nResolution = globalInfo.res + 1;
  201.    nBPP = globalInfo.bpp + 1;
  202.  
  203.    /* read background color */
  204.    if (![self readBytes:1 data:&b1]) return( NO );
  205.    //   GlobalInfo.nBackground = b1;
  206.  
  207.    /* reserved zero at end of screen descriptor */
  208.    if (![self readBytes:1 data:&b1]) return( NO );
  209.    
  210.    /* read in the global color map, if it exists */
  211.    if (globalInfo.map) [self readMap];
  212.  
  213.    /* everything loaded OK, return TRUE */
  214.    nStatus = STATUS_BLOCK;
  215.    return( YES );     
  216.    } /* readGlobalInfo 10/16/89 CFS */
  217.  
  218.  
  219. /***************************************************************************/
  220. /* readMap - read a color map from the stream                   */
  221. /***************************************************************************/
  222. - (BOOL)readMap
  223.    {
  224.    nNumColors = 1;
  225.    nNumColors <<= nBPP;  /*2^BPP*/
  226.    [numColors setIntValue:nNumColors];
  227.    if (![self readBytes:nNumColors * 3 data:byColorMap]) return( NO );
  228.    return( YES );
  229.    } /* readMap 1/23/90 CFS */
  230.  
  231.  
  232. /***************************************************************************/
  233. /* readBlockCode - read the next character blockcode and set status       */
  234. /***************************************************************************/
  235. - (BOOL)readBlockCode
  236.    {
  237.    char  cBlockCode;
  238.  
  239.    if (![self readBytes:1 data:&cBlockCode]) return( NO );
  240.    switch (cBlockCode)
  241.       {
  242.       case ',': nStatus = STATUS_IMAGE;      break;
  243.       case '!': nStatus = STATUS_EXTENSION;  break;
  244.       case ';': nStatus = STATUS_DONE;       break;
  245.       default:
  246.          {
  247.          [self errorAlert:"Bad block code in readNextBlockCode."];
  248.          [self cancelDecoding];
  249.          return( NO );
  250.        }
  251.       }   
  252.    return( YES );
  253.    } /* readBlockCode 1/23/90 CFS */
  254.  
  255.  
  256. /***************************************************************************/
  257. /* readImageInfo - read the image information header from the file       */
  258. /***************************************************************************/
  259. - (BOOL)readImageInfo
  260.    {
  261.    BYTE    b1, b2;
  262.    struct    {
  263.         unsigned  map:1;
  264.         unsigned  interlace:1;
  265.         unsigned  reserved:3;
  266.         unsigned  bpp:3;
  267.         } localInfo;
  268.    
  269.    /* read the image margins and size */                  
  270.    if (![self readBytes:1 data:&b1]) return( NO );
  271.    if (![self readBytes:1 data:&b2]) return( NO );
  272.    //   ImageInfo.nLeftMargin = b2 * 256 + b1;
  273.    if (![self readBytes:1 data:&b1]) return( NO );
  274.    if (![self readBytes:1 data:&b2]) return( NO );
  275.    //   ImageInfo.nTopMargin = b2 * 256 + b1;
  276.    if (![self readBytes:1 data:&b1]) return( NO );
  277.    if (![self readBytes:1 data:&b2]) return( NO );
  278.    nWidth = b2 * 256 + b1;
  279.    [width setIntValue:nWidth];
  280.    if (![self readBytes:1 data:&b1]) return( NO );
  281.    if (![self readBytes:1 data:&b2]) return( NO );
  282.    nHeight = b2 * 256 + b1;
  283.    [height setIntValue:nHeight];
  284.  
  285.    /* read the image info byte */  
  286.    if (![self readBytes:1 data:&localInfo]) return( NO );
  287.    bInterlaced = localInfo.interlace;
  288.    //   ImageInfo.nBPP = localInfo.bpp + 1;
  289.  
  290.    if (localInfo.map) [self readMap];
  291.  
  292.    /* everything loaded OK, return TRUE */
  293.    nStatus = STATUS_DECODING;
  294.    return( YES );     
  295.    } /* readImageInfo 10/16/89 CFS */
  296.  
  297.  
  298. /***************************************************************************/
  299. /* startDecoder - control decoding of the image                   */
  300. /***************************************************************************/
  301. - (BOOL)startDecoder
  302.    {
  303.    BYTE      byCodeSize;
  304.  
  305.    [decodePanel setTitle:"Decoding Status - decoding..."];
  306.    /* read the beginning code size - should be same as BPP */
  307.    if (![self readBytes:1 data:&byCodeSize]) return( NO );
  308.   
  309.    /* free any previous image data & allocate space for the current image */
  310.    free( byDataR );
  311.    free( byDataG );
  312.    free( byDataB );
  313.    byDataR = malloc( nWidth * nHeight );
  314.    byDataG = malloc( nWidth * nHeight );
  315.    byDataB = malloc( nWidth * nHeight );
  316.  
  317.    /* set up the time left gauge */
  318.    [gauge setMinValue:0];
  319.    [gauge setMaxValue:nHeight * nWidth];
  320.    [gauge setIntValue:0];
  321.    
  322.    /* start the LZW decoder */
  323.    StartLZW( byCodeSize, stream, byDataR, byDataG, byDataB, byColorMap,
  324.              nWidth, nHeight, bInterlaced );
  325.    bDecoding = YES;
  326.  
  327.    return( YES );     
  328.    } /* startDecoder 1/24/90 CFS */
  329.  
  330.  
  331. /***************************************************************************/
  332. /* finishDecoder: - clean up after the LZW process                */
  333. /***************************************************************************/
  334. - (BOOL)finishDecoder:(long)lReturnCode
  335.    {
  336.    NXRect    nxrBit;
  337.      
  338.    if (lReturnCode == -1) [self errorAlert:
  339.       "The whole image couldn't be loaded.\nAs much as possible will be shown."];
  340.       
  341.    [gauge setFloatValue:(float)(nWidth * nHeight)];
  342.    /* create a bitmap, and image the data into it */
  343.    [decodePanel setTitle:"Decoding Status - imaging..."];
  344.    nxrBit.size.width = nWidth;
  345.    nxrBit.size.height = nHeight;
  346.    bmpOut = [Bitmap newSize:nxrBit.size.width :nxrBit.size.height
  347.                     type:NX_UNIQUEBITMAP];
  348.    [bmpOut setFlip:NO];
  349.    [bmpOut lockFocus];
  350.    NXImageBitmap( &nxrBit, nWidth, nHeight, 8, 3, NX_PLANAR,
  351.                   NX_COLORMASK, byDataR, byDataG, byDataB, NULL, NULL );
  352.    [bmpOut unlockFocus];
  353.    [decodePanel setTitle:"Decoding Status"];
  354.     
  355.    bDecoding = NO;
  356.    nStatus = STATUS_DONE;
  357.    
  358.    //  ALLOW MULTIPLE IMAGES EVENTUALLY
  359.    //   /* read the last block count - should be zero */
  360.    //   if (![self readBytes:1 data:&byDummy]) return( NO );
  361.    //   nStatus = STATUS_BLOCK;
  362.    
  363.   return( YES );
  364.   } /* finishDecoder: 1/24/90 CFS */
  365.   
  366.  
  367. /***************************************************************************/
  368. /* sucessfulDecoding - send decoded image to controller               */
  369. /***************************************************************************/
  370. - sucessfulDecoding
  371.    {
  372.    [timer free];
  373.    NXCloseMemory( stream, NX_FREEBUFFER );
  374.    [target perform:action with:bmpOut];
  375.    return( self );
  376.    } /* sucessfulDecoding 1/23/90 CFS */
  377.  
  378.  
  379. /***************************************************************************/
  380. /* cancelDecoding - notify controller that file couldn't be decoded        */
  381. /***************************************************************************/
  382. - cancelDecoding
  383.    {
  384.    [timer free];
  385.    NXCloseMemory( stream, NX_FREEBUFFER );
  386.    [target perform:action with:nil with:(id)NO];
  387.    return( self );
  388.    } /* cancelDecoding 1/23/90 CFS */
  389.  
  390.  
  391. /***************************************************************************/
  392. /* readBytes - read in the desired number of data bytes               */
  393. /***************************************************************************/
  394. - (BOOL)readBytes:(int)nNumBytes data:(void *)buf
  395.    {
  396.    int   nError;
  397.  
  398.    nError = NXRead( stream, buf, nNumBytes );
  399.    if (nError <= 0)
  400.       {
  401.       [self errorAlert:"File couldn't be completely read."];
  402.       [self cancelDecoding];
  403.       return( NO );
  404.       }
  405.    return( YES );   
  406.    } /* readBytes 1/23/90 CFS */
  407.    
  408.    
  409. /***************************************************************************/
  410. /* errorAlert - put the message in an alert panel               */
  411. /***************************************************************************/
  412. - errorAlert:(char *)szMessage
  413.    {
  414.    NXRunAlertPanel( "ViewGif2 Error", szMessage, "OK", NULL, NULL );
  415.    return( self );
  416.    } /* errorAlert 10/30/89 CFS */
  417.  
  418. @end
  419.